package cn.kduck.module.account.web;

import cn.kduck.core.cache.CacheHelper;
import cn.kduck.core.service.ParamMap;
import cn.kduck.core.web.annotation.ModelOperate;
import cn.kduck.core.web.annotation.ModelResource;
import cn.kduck.core.web.json.JsonObject;
import cn.kduck.module.account.AccountConfig;
import cn.kduck.module.account.AccountConfig.CredentialConfig;
import cn.kduck.module.account.credential.CredentialGenerator;
import cn.kduck.module.account.service.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;

import java.security.SecureRandom;
import java.util.Map;
import java.util.UUID;

@RestController
@RequestMapping("/account/credential")
@ModelResource("帐号凭证管理")
public class CredentialController {

    private final Log logger = LogFactory.getLog(getClass());

    @Autowired
    private CredentialService credentialService;

    @Autowired
    private AccountConfig accountConfig;

    @Autowired
    private AccountService accountService;

    @Autowired
    private AccountCredentialProvider accountCredentialProvider;

    @Autowired(required = false)
    private RetrieveCaptchaSender retrieveCaptchaSender;

    @GetMapping("/retrieve/captcha")
    public JsonObject retrieveCaptcha(@RequestParam("phoneNum") String phoneNum){

        if(retrieveCaptchaSender == null){
            throw new RuntimeException("未配置密码找回的短信发送器，请实现cn.kduck.module.account.service.RetrieveCaptchaSender接口并声明为一个SpringBean");
        }

        //根据phoneNum查询用户，如果不存在则返回错误信息，而不进行短信发送
        AccountUser user = accountCredentialProvider.getUserByPhone(phoneNum);
        if(ObjectUtils.isEmpty(user)){
            return new JsonObject(null,-1,"账户不存在");
        }

        Object sleep = CacheHelper.getByCacheName(CredentialController.class.getSimpleName(), phoneNum + "#SLEEP");

        if(!ObjectUtils.isEmpty(sleep)){
            return new JsonObject(null,-1,"获取验证码操作太频繁，请稍后再试");
        }
        String captcha = randomCaptcha(6);

        try{
            retrieveCaptchaSender.sendCaptcha(captcha,phoneNum);
        }catch (Exception e){
            logger.error("发送短信验证码失败",e);
            return new JsonObject(null,-1,"发送短信验证码失败");
        }


        CacheHelper.put(CredentialController.class.getSimpleName() , phoneNum,captcha,300);
        CacheHelper.put(CredentialController.class.getSimpleName() , phoneNum+"#SLEEP",captcha,60);
        return JsonObject.SUCCESS;
    }

    @PostMapping("/retrieve/captcha/valid")
    public JsonObject validCaptcha(@RequestParam("type") Integer type,@RequestParam("account") String account,@RequestParam("captcha") String captcha){

        String savedCaptcha = (String)CacheHelper.getByCacheName(CredentialController.class.getSimpleName(), account);

        if(savedCaptcha  == null || !savedCaptcha.equals(captcha)){
            return new JsonObject(null,-1,"验证码验证失败");
        }

        AccountUser user = type.intValue() == RetrieveType.PHONE.getType() ? accountCredentialProvider.getUserByPhone(account) : accountCredentialProvider.getUserByEmail(account);

        if(user == null){
            return new JsonObject(null,-1,"账号不存在");
        }

        String verifyCode = UUID.randomUUID().toString();
        CacheHelper.put(CredentialController.class.getSimpleName() , user.getUserId() + "#VERIFYCODE",verifyCode,300);

        String userName = user.getUserName();

        Map<String, Object> dataMap = ParamMap.create("userId", user.getUserId())
                .set("verifyCode", verifyCode)
                .set("userName", fuzzyUserName(userName))
                .toMap();
        return new JsonObject(dataMap);
    }

    private String fuzzyUserName(String userName){
        char[] userNameChars = userName.toCharArray();
        return "*" + userNameChars[userName.length() - 1];
    }

    @PostMapping("/retrieve")
    public JsonObject retrievePassword(@RequestParam("userId") String userId,@RequestParam("verifyCode") String verifyCode,@RequestParam("newPassword") String newPassword){

        String savedVerifyCode = (String)CacheHelper.getByCacheName(CredentialController.class.getSimpleName(), userId + "#VERIFYCODE");

        if(ObjectUtils.isEmpty(savedVerifyCode)){
            return new JsonObject(null,-1,"验证码失效");
        }

        if(savedVerifyCode.equals(verifyCode)){
            Account account = accountService.getAccountByUserId(userId);
            accountService.changePassword(account.getAccountName(),newPassword);
            return JsonObject.SUCCESS;
        }else{
            return new JsonObject(null,-1,"验证码无效");
        }
    }

    @GetMapping("/valid")
    @ModelOperate(name="帐号凭证强度检测")
    public JsonObject isValid(@RequestParam("credential") String credential){

        JsonObject jsonObject = new JsonObject();

        checkLength(jsonObject,credential);

        if(!Boolean.parseBoolean(""+jsonObject.getData())){
            return jsonObject;
        }

        boolean pass = credentialService.checkCredential(credential);
        if(!pass){
            checkCredential(jsonObject, pass);

        }
        return jsonObject;
    }

    private void checkCredential(JsonObject jsonObject, boolean pass) {
        CredentialConfig credentialConfig = accountConfig.getCredentialConfig();
        Integer[] strength = credentialConfig.getStrength();
        StringBuilder messageBuilder = new StringBuilder("密码强度不符合要求，必须包含：");

        if(strength.length > 0){
            int strengthValue = 0;
            for (Integer s : strength) {
                strengthValue += s;
            }

            if((strengthValue & CredentialGenerator.NUMBER) >0){
                messageBuilder.append("数字、");
            }
            if((strengthValue & CredentialGenerator.UPPER_LETTER) >0){
                messageBuilder.append("大写字母、");
            }
            if((strengthValue & CredentialGenerator.LOWER_LETTER) >0){
                messageBuilder.append("小写字母、");
            }
            if((strengthValue & CredentialGenerator.SPECIAL) >0){
                messageBuilder.append("特殊字符、");
            }
            String errorMessage = messageBuilder.toString();
            jsonObject.setMessage(errorMessage.substring(0,errorMessage.length()-1));
        }
        jsonObject.setData(pass);
    }

    private void checkLength(JsonObject jsonObject,String credential){

        CredentialConfig credentialConfig = accountConfig.getCredentialConfig();
        int length = credential.trim().length();

        StringBuilder messageBuilder = new StringBuilder("密码强度不符合要求，必须包含");
        boolean pass = true;
        if(credentialConfig.getMinLength() > 0 && length < credentialConfig.getMinLength()){
            messageBuilder.append("长度最少" + credentialConfig.getMinLength() + "个字符");
            pass = false;
        }
        if(credentialConfig.getMaxLength() > 0 && length > credentialConfig.getMaxLength()){
            messageBuilder.append("长度最多" + credentialConfig.getMaxLength() + "个字符");
            pass = false;
        }

        jsonObject.setData(pass);
        if(!pass){
            jsonObject.setMessage(messageBuilder.toString());
        }
    }

    private String randomCaptcha(int len){
        StringBuilder captchaBuilder = new StringBuilder();
        SecureRandom secureRandom = new SecureRandom();
        for (int i = 0; i < len; i++) {
            captchaBuilder.append(secureRandom.nextInt(10));
        }
        return captchaBuilder.toString();
    }


}
